/*******************************************************************************
 * @file fdt_vector.c
 * @brief  inter process Fast Data Transfer
 *
 *
 * Author: Chrisx
 *
 * Copyright (c) 2016 Chrisx. All Rights Reserved.
 *
 * CONFIDENTIALITY AND LIMITED USE
 *
 * This software, including any software of third parties embodied herein,
 * contains information and concepts which are confidential to Chrisx
 * and such third parties. This software is licensed for use
 * solely in accordance with the terms and conditions of the applicable
 * license agreement with Kevinx or its authorized distributor.
 ************************************************************************************/
#include "fdt_vector.h"

static int named_shared_memory_create(const char* name, unsigned long size, void** base_addr)
{
    char filename[64] = {0};
    sprintf(filename, "/%s.shm", name);

    int fd = shm_open(filename, O_CREAT | O_RDWR, 00666);
    if (fd < 0) {
        printf("shm_open fail: errno=[%d:%s]\n", errno, strerror(errno));
        *base_addr = NULL;
        return -1;
    }

    // align to page boundary
    size += 0xFFF;
    size &= 0xFFFFF000;
    if (ftruncate(fd, size) < 0) {
        printf("ftruncate fail: errno=[%d:%s]\n", errno, strerror(errno));
        *base_addr = NULL;
        close(fd);
        return -1;
    }

    // mmap into memory
    *base_addr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (*base_addr == MAP_FAILED) {
        printf("mmap fail: errno=[%d:%s]\n", errno, strerror(errno));
        *base_addr = NULL;
        close(fd);
        return -1;
    }  

    return fd;  
}

static int named_shared_memory_destory(int fd, const char* name, unsigned long size, void* base_addr)
{
    char filename[64] = {0};
    sprintf(filename, "/%s.shm", name);

    munmap(base_addr, size);
    shm_unlink(filename);
    close(fd);    

    return 0;
}

#if 0
static void* make_shared_memory(const char* name, unsigned long size)
{
     void* base_addr = NULL;
     named_shared_memory_create(name, size, &base_addr);

     return base_addr;
}
#endif

int fdt_unlock(int fd, unsigned int start, unsigned int off)
{
    struct flock lock;
    
    lock.l_type = F_UNLCK;
    lock.l_whence = SEEK_SET;
    lock.l_start = start;
    lock.l_len = off;

    return fcntl(fd, F_SETLKW, &lock);
}

int fdt_wr_lock(int fd,  unsigned int start, unsigned int off)
{
    struct flock lock;
    
    lock.l_type = F_WRLCK;
    lock.l_whence = SEEK_SET;
    lock.l_start = start;
    lock.l_len = off;

    return fcntl(fd, F_SETLKW, &lock);
}

int fdt_wr_trylock(int fd,  unsigned int start, unsigned int off)
{
    struct flock lock;
    
    lock.l_type = F_WRLCK;
    lock.l_whence = SEEK_SET;
    lock.l_start = start;
    lock.l_len = off;

    return fcntl(fd, F_SETLK, &lock);
}

int fdt_rd_lock(int fd,  unsigned int start, unsigned int off)
{
    struct flock lock;
    
    lock.l_type = F_RDLCK;
    lock.l_whence = SEEK_SET;
    lock.l_start = start;
    lock.l_len = off;

    return fcntl(fd, F_SETLKW, &lock);
}

int fdt_rd_trylock(int fd,  unsigned int start, unsigned int off)
{
    struct flock lock;
    
    lock.l_type = F_RDLCK;
    lock.l_whence = SEEK_SET;
    lock.l_start = start;
    lock.l_len = off;

    return fcntl(fd, F_SETLK, &lock);
}

int fdt_vector_create(struct fdt_vector* vec)
{
    vec->fd = named_shared_memory_create(vec->name, vec->size*vec->obj_size, &vec->base_addr);
    if (vec->fd > 0) {
        if (vec->constructor != NULL)
            vec->constructor(vec);

        return 0;
    } else
        return -1;
}

int fdt_vector_destory(struct fdt_vector* vec)
{
    if (vec->destructor != NULL) {
        vec->destructor(vec);
    }

    named_shared_memory_destory(vec->fd, vec->name, vec->size*vec->obj_size, vec->base_addr);

    return 0;
}

int fdt_vector_resize(struct fdt_vector* vec, unsigned int new_size, int maymove)
{
    void* new_addr;
    int flags = 0;

    if (maymove == 1)
        flags |= MREMAP_MAYMOVE;

    new_addr = mremap(vec->base_addr, vec->size*vec->obj_size, new_size*vec->obj_size, flags);
    if (new_addr == MAP_FAILED) {
        return -1;
    }
    
    vec->base_addr = new_addr;
    vec->size = new_size;

    return 0;
}

void* fdt_get_elem_pointer(struct fdt_vector* vec,
                           int i,
                           int (*lock)(int, unsigned int, unsigned int))
{
    unsigned int start = i*vec->obj_size;
    unsigned int off = vec->obj_size;
    int ret;

    if (i >= (int)vec->size)
        return NULL;

    ret = lock(vec->fd, start, off);
    if (ret == -1)
        return NULL;

    return (void*)((char*)vec->base_addr + start);
}

void fdt_put_elem_pointer(struct fdt_vector* vec,
                          int i,
                          int (*unlock)(int, unsigned int, unsigned int))
{
    unsigned int start = i*vec->obj_size;
    unsigned int off = vec->obj_size;
    
    if (i >= (int)vec->size)
        return;

    unlock(vec->fd, start, off);    
}

